Skip to content

Supports Anymal-D Rough terrain training in newton#5225

Closed
ooctipus wants to merge 2 commits intoisaac-sim:developfrom
ooctipus:newton_rough_terrain
Closed

Supports Anymal-D Rough terrain training in newton#5225
ooctipus wants to merge 2 commits intoisaac-sim:developfrom
ooctipus:newton_rough_terrain

Conversation

@ooctipus
Copy link
Copy Markdown
Collaborator

Description

This PR depends on

#5219
#5179
#5098

Note:

  • we still have to disable randomize_rigid_body_com, as turning it on will causes leviating robot
  • Tobias is working on supporting newton with any size of triangle, if we merge after tobias fix, we can remove the densifying triangle logic in terrain generator.

Type of change

  • New feature (non-breaking change which adds functionality)

Screenshots

Please attach before and after screenshots of the change if applicable.

Checklist

  • I have read and understood the contribution guidelines
  • I have run the pre-commit checks with ./isaaclab.sh --format
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • I have updated the changelog and the corresponding version in the extension's config/extension.toml file
  • I have added my name to the CONTRIBUTORS.md or my name already exists there

@github-actions github-actions Bot added documentation Improvements or additions to documentation isaac-mimic Related to Isaac Mimic team labels Apr 10, 2026
Copy link
Copy Markdown
Contributor

@kellyguo11 kellyguo11 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review: Supports Anymal-D Rough Terrain Training in Newton

Overview

This is a large, multi-feature PR (+5344/-3744 across 87 files) that bundles together several cross-cutting changes needed for Newton-based rough terrain locomotion training. The scope spans:

  1. Newton XformPrimView (NewtonSiteXformPrimView) — GPU-resident site-based transform tracking via Warp kernels
  2. Fabric XformPrimView (FabricXformPrimView) — Fabric-accelerated variant for PhysX backend
  3. XformPrimView contract tests (xform_contract_tests.py) — Shared test invariants for all backends
  4. Newton collision pipeline config (NewtonCollisionPipelineCfg, HydroelasticSDFCfg) — Expose Newton collision parameters
  5. Friction/restitution randomizationset_friction_index/mask, set_restitution_index/mask for Newton assets + set_material_properties_index/mask for PhysX
  6. Material randomization refactor — Backend-aware randomize_rigid_body_material + new randomize_rigid_body_inertia + refactored randomize_rigid_body_collider_offsets
  7. RayCaster sensor spawningRayCasterXformCfg so sensors get their own non-physics Xform (Newton requires this)
  8. New terminationsbody_lin_vel_out_of_limit, body_ang_vel_out_of_limit
  9. Task config updates — All locomotion rough-terrain configs updated for raycaster child paths + Newton solver configs for AnymalD
  10. Velocity env restructureStartupEventsCfg merged into EventsCfg, events no longer MISSING

Should-Fix Issues

1. Version mismatch in isaaclab_newton extension.toml vs CHANGELOG

extension.toml sets version = "0.5.11", but the CHANGELOG has entries for 0.5.11, 0.5.12, and 0.5.13. The version in extension.toml should match the latest changelog entry (0.5.13).

2. Changelog entry for isaaclab_physx version ordering inconsistency

The CHANGELOG adds entries for 0.5.15 and 0.5.14, but extension.toml is bumped from 0.5.130.5.14. The 0.5.15 changelog entry documents work in this PR but there's no corresponding version bump, suggesting either the entry should be 0.5.14 or the toml needs to go to 0.5.15.

3. velocity_env_cfg.py: events field changed from MISSING to EventsCfg()

LocomotionVelocityRoughEnvCfg.events changes from MISSING to EventsCfg(). This is a breaking change — all downstream configs that previously had to supply events (like AnymalB, AnymalC, Spot, etc. not shown in this PR) will now silently get the base defaults. Configs that override events with their own dataclass (e.g., the old AnymalDPhysxEventsCfg(EventsCfg, StartupEventsCfg) pattern) are no longer needed since startup events moved into EventsCfg, but any external config relying on MISSING to enforce explicit event configuration will break silently.

Additionally, StartupEventsCfg is removed. Code outside the repo that inherits from it will fail at import.

4. randomize_rigid_body_material: Newton path uses set_friction_mask / set_restitution_mask (full data every call)

In _call_newton, the code always passes the full self.default_friction / self.default_restitution tensors via set_*_mask(), even when only a subset of environments changed. This writes the entire (num_instances × num_shapes) buffer every call. Consider using set_friction_index / set_restitution_index with only the env_ids that need updating, similar to how the PhysX path passes env_ids to set_material_properties_index.

5. NewtonSiteXformPrimView._gather_scales kernel has O(N×S) complexity

The _gather_scales Warp kernel does a linear scan over all shapes for every site (O(sites × shapes)). For articulations with hundreds of shapes, this is slow. Consider building a precomputed site_body → first_shape index at init time.


Suggestions

6. PR scope — consider splitting

This PR bundles at least 5 independent features. Each would be easier to review and less risky to merge independently:

  • XformPrimView backends + contract tests
  • Collision pipeline config
  • Friction/restitution APIs + material randomization refactor
  • RayCaster spawning refactor
  • AnymalD Newton config + velocity env restructure

7. randomize_rigid_body_collider_offsets — Newton path accesses internal attributes

The Newton implementation reads/writes shape_margin and shape_gap via root_view.get_attribute() / root_view.set_attribute() with SimulationManager.get_model(). This couples the randomization term to Newton internals. A TODO comment acknowledging this would help.

8. base_com preset uses newton=None in velocity_env_cfg

The PR disables CoM randomization for Newton (base_com = preset(default=EventTerm(...), newton=None)). The PR description mentions "we still have to disable randomize_rigid_body_com, as turning it on will cause levitating robot". This should be documented as a known limitation with a TODO or link to a tracking issue.

9. Test comparison with Isaac Sim — reduced coverage

test_compare_get_world_poses_with_isaacsim and the remaining Isaac Sim comparison tests were heavily simplified — quaternion comparisons removed, local pose and set-pose comparisons deleted entirely. The contract tests partially compensate, but backend-specific behavioral parity with Isaac Sim is no longer tested.

10. UsdXformPrimView.set_world_poses — partial-update edge case

When only positions is provided (without orientations), the code reads the current world transform, replaces translation, then decomposes back to local. This round-trip through Gf.Matrix4d may accumulate floating-point drift over many calls. Consider documenting this or caching.

11. Documentation checkbox unchecked

The PR checklist shows [ ] I have made corresponding changes to the documentation. The raycaster tutorial docs are updated, but the new APIs (set_friction_index, NewtonCollisionPipelineCfg, RayCasterXformCfg, new terminations, randomize_rigid_body_inertia) should have corresponding API docs or migration guide entries.


Positive Notes

  • The contract test pattern (xform_contract_tests.py with ViewBundle fixture) is excellent engineering — ensures all backends satisfy the same invariants
  • The NewtonSiteXformPrimView design is clean: ancestor-walk resolution at init, GPU-only pose compute at runtime
  • The raycaster spawning refactor (RayCasterXformCfg) is a good pattern — decoupling sensor attachment from physics prims
  • Backend-aware material randomization with clean PhysX/Newton dispatch
  • Good deprecation handling with auto-redirect for legacy raycaster paths

@hujc7
Copy link
Copy Markdown

hujc7 commented Apr 17, 2026

Ablation Study Results — PR #5225 vs PR #5248 mechanism overlap

TL;DR: The collider_offsets startup event in this PR overlaps with PR #5248's default_shape_margin and is counterproductive when both are applied — they both target Newton's ShapeConfig.gap and the event clobbers the margin. Removing the event gives +3.7 reward on Anymal-D Newton (A3: +16.38 vs A0 baseline +12.47). The base_com = preset(newton=None) gate is critical (without it 99.99 % of episodes terminate from body lin vel explosion). The terrain border rewrite gives a small (~+1.8) benefit. The NaN termination is unused.

Ran a 6-variant ablation on Anymal-D Newton (Newton 1.1.0 — this PR's pin), 300 iter each, 4096 envs, single-variable changes via worktrees. Findings on which #5225 features are load-bearing, and how they interact with PR #5248's default_shape_margin.

Setup

Results

# Variant Removed feature Reward Δ vs baseline
A0 baseline (all features) +12.47 ref
A1 no NaN termination body_lin_vel_out_of_limit DoneTerm +12.94 +0.47
A2 old terrain border grid-strip border (revert to make_border boxes) +10.71 -1.76
A3 no collider_offsets event startup randomize_rigid_body_collider_offsets +16.38 +3.71
A4 Newton CoM unlocked preset(default=..., newton=None) gate on base_com -0.05 CRASH
A5 no default_shape_margin PR #5248's NewtonCfg.default_shape_margin=0.01 +10.89 -1.58

Interpretations

A4 (CRASH): confirmed base_com = preset(newton=None) is critical. Without the Newton-gate, Episode_Termination/body_lin_vel: 0.9999 — 99.99% of episodes terminate from body lin vel exploding past 20 m/s. The gate must stay.

A3 vs A5 (the interesting pair): both collider_offsets event (this PR) and default_shape_margin (#5248) write to Newton's ShapeConfig.gap. They are not additive:

The event computes gap = max(0, contact_offset − initial_margin). With #5248's default_shape_margin=0.01 setting ShapeConfig.margin=0.01, the event's contribution becomes max(0, 0.01 − 0.01) = 0 — so the event resets gap to 0 after margin was set, hurting contact quality. Without the event, default_shape_margin keeps gap=0.01 cleanly via #5289's fix.

A1 (NaN termination): never fires on Anymal-D normal training (already covered by base_contact). Safe to remove.

A2 (terrain border): subdivided grid border gives ~+1.8 reward over make_border boxes. Real benefit but small.

Suggested changes to this PR

  1. Remove collider_offsets startup event — superseded by [Newton] Add Rough terrain locomotion Part 1: Foundation + quadrupeds on Newton #5248's default_shape_margin. Expected +3.7 reward gain on Anymal-D.
  2. Keep base_com = preset(default=..., newton=None) — critical Newton stability fix.
  3. Keep the terrain border rewrite — minor but real.
  4. Optional: drop body_lin_vel_out_of_limit termination — not load-bearing on quadrupeds (may still matter as NaN guard for other robots; cheap to keep).

Caveats / scope

cc @camevor @AntoineRichard

@hujc7
Copy link
Copy Markdown

hujc7 commented Apr 22, 2026

FYI @ooctipus — the root cause of the base_com collapse on Newton that motivated the preset(newton=None) WAR in this PR has been identified and fixed upstream.

Root cause

convert_warp_coords_to_mj_kernel in Newton's MuJoCo solver used per-world joint index for joint_type/joint_child/joint_dof_dim instead of the global index. With 4096 envs, only world 0 got the correct joint-child → body mapping; the other 4095 envs read stale data and applied wrong CoM offsets → immediate balance loss after any runtime body_com write (what randomize_rigid_body_com does via set_coms_index + notify_model_changed(BODY_INERTIAL_PROPERTIES)).

Upstream

Verification (2026-04-22, this VM)

End-to-end with PR #2332 applied + base_com event re-enabled on Newton:

Configuration Anymal-D rough, 300 iter, 4096 envs ep length
Newton 1.1.0 + base_com enabled (current failure mode) −0.05 (collapse) <100
Newton 1.1.0 + this PR's WAR +12.47 ~900
Newton PR #2332 + base_com enabled +14.66 887

Fix not only prevents the collapse — it exceeds the WAR baseline by +17%.

Follow-up

Once PR #2332 merges and ships in a Newton release, the preset(newton=None) WAR on base_com in this PR can be dropped so randomize_rigid_body_com runs uniformly across backends. No urgent action here — just flagging so the TODO is tracked.

@kellyguo11 kellyguo11 marked this pull request as ready for review April 23, 2026 18:43
@kellyguo11 kellyguo11 requested a review from Mayankm96 as a code owner April 23, 2026 18:43
Copy link
Copy Markdown

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Isaac Lab Review Bot

Summary

This PR adds Newton physics backend support for Anymal-D rough terrain training by: (1) adding new body velocity termination functions, (2) rewriting the terrain border generation to use finer-grained meshes for Newton compatibility, (3) refactoring event configurations to use preset patterns for backend-specific settings, and (4) adding Newton-specific physics configuration. The implementation is mostly correct but has several issues that need attention.

Architecture Impact

  • terminations.py: New body_lin_vel_out_of_limit and body_ang_vel_out_of_limit functions are public API additions. Any environment can now use these for early termination on physics solver instabilities.
  • terrain_generator.py: The border generation change affects ALL terrain generation, not just Newton. The old make_border function is no longer imported, which could break external code depending on that import pattern.
  • velocity_env_cfg.py: The StartupEventsCfg class is removed, which is a breaking change for any code that imports or inherits from it. The EventsCfg now has startup events with preset patterns for backend selection.
  • rough_env_cfg.py: The AnymalDPhysxEventsCfg and AnymalDEventsCfg classes are removed in favor of RoughPhysicsCfg.

Implementation Verdict

Minor fixes needed — there are correctness issues with the terrain border implementation and potential device mismatches in termination functions.

Test Coverage

The PR claims tests were added ("I have added tests that prove my fix is effective") but no test files are included in the diff. For a feature PR adding:

  • New termination functions (need unit tests)
  • Modified terrain generation (need regression tests)
  • New physics backend support (need integration tests)

This is a significant gap.

CI Status

No CI checks available yet.

Findings

🔴 Critical: source/isaaclab/isaaclab/terrains/terrain_generator.py:300 — Border strips have gaps at corners

The four border strips are constructed as:

strips = [
    _make_grid_strip(-bw, -bw, inner_w + 2 * bw, bw),  # bottom
    _make_grid_strip(-bw, inner_l, inner_w + 2 * bw, bw),  # top
    _make_grid_strip(-bw, 0.0, bw, inner_l),  # left
    _make_grid_strip(inner_w, 0.0, bw, inner_l),  # right
]

The left/right strips start at y=0.0 and have length inner_l, meaning they don't extend into the corners. The corners are covered by the top/bottom strips, but the left/right strips have a gap from y=-bw to y=0 and from y=inner_l to y=inner_l+bw. The bottom strip starts at y=-bw with height bw, ending at y=0. The left strip starts at y=0, so there's no overlap issue - this is actually correct. Disregard.

🔴 Critical: source/isaaclab/isaaclab/terrains/terrain_generator.py:269-310 — Border height is ignored, terrain border is now flat at z=0

The old implementation used self.cfg.border_height to create a 3D border with configurable height. The new implementation creates a flat mesh at z=0:

vertices = np.zeros((nx * ny, 3))  # z is always 0

This silently changes behavior for any configuration that relied on border_height being non-zero. Robots could now walk off the edge of the terrain instead of hitting a wall.

🟡 Warning: source/isaaclab/isaaclab/envs/mdp/terminations.py:166,180 — Missing body_ids initialization check

Both new termination functions use asset_cfg.body_ids directly without checking if it's None:

body_vel = wp.to_torch(asset.data.body_lin_vel_w)[:, asset_cfg.body_ids]

When body_ids is None, this will select the first column only (not all columns). Compare with joint_pos_out_of_limit at line 90 which handles this:

if asset_cfg.joint_ids is None:
    asset_cfg.joint_ids = slice(None)

The same pattern should be applied for body_ids.

🟡 Warning: source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/velocity_env_cfg.py — Breaking API change: StartupEventsCfg removed

The class StartupEventsCfg was removed without deprecation. Any external code importing this class will break:

from isaaclab_tasks.manager_based.locomotion.velocity.velocity_env_cfg import StartupEventsCfg  # Now fails

🟡 Warning: source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/velocity_env_cfg.py:298-300 — Termination uses body_names=".*" which may be expensive

body_lin_vel = DoneTerm(
    func=mdp.body_lin_vel_out_of_limit,
    params={"max_velocity": 20.0, "asset_cfg": SceneEntityCfg("robot", body_names=".*")},
)

This checks ALL bodies on every step. For robots with many links, this adds overhead. Consider limiting to a subset (e.g., body_names="base") if only checking for solver explosions.

🔵 Improvement: source/isaaclab/isaaclab/terrains/terrain_generator.py:282-296 — Face generation loop could use vectorized operations

The nested loop for face generation is slow for large borders:

faces = []
for i in range(nx - 1):
    for j in range(ny - 1):
        ...
        faces.append([v0, v2, v1])
        faces.append([v1, v2, v3])

This could be vectorized with numpy for better performance:

i, j = np.meshgrid(np.arange(nx-1), np.arange(ny-1), indexing='ij')
v0 = (i * ny + j).flatten()
# ... vectorized face construction

🔵 Improvement: source/isaaclab/isaaclab/envs/mdp/terminations.py:159-184 — Missing type hints in TYPE_CHECKING block

The new functions use Articulation in type hints, but the import is only under TYPE_CHECKING. The functions will work at runtime, but static analysis may not recognize the type properly. This matches existing code style, so this is minor.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 23, 2026

Greptile Summary

This PR adds Newton physics backend support for Anymal-D rough terrain locomotion training by replacing the event-based physics selection with a SimulationCfg-based preset, adding body velocity terminations for solver-singularity detection, and replacing the box-primitive terrain border with a subdivided flat-grid border to avoid large-triangle collision failures in Newton/MuJoCo.

  • P1 – collider_offsets will fail on Newton: The new collider_offsets startup event calls asset.root_view.get_contact_offsets() / set_contact_offsets(), which are PhysX-specific methods absent from Newton's ArticulationView. Unlike base_com, it is not wrapped in preset(default=..., newton=None), so Newton runs will hit an AttributeError at startup.

Confidence Score: 4/5

Not safe to merge without addressing the collider_offsets Newton compatibility issue, which will cause a startup crash in Newton mode.

One P1 finding: the collider_offsets event uses PhysX-only root_view methods without a Newton guard, which will raise an AttributeError at environment startup when the Newton backend is selected — the primary use-case this PR introduces. All other changes are sound.

source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/velocity_env_cfg.py — collider_offsets event needs newton=None guard

Important Files Changed

Filename Overview
source/isaaclab/isaaclab/envs/mdp/terminations.py Adds body_lin_vel_out_of_limit and body_ang_vel_out_of_limit; uses wp.to_torch correctly on the wp.vec3f Warp array and handles NaN/overflow via isnan check.
source/isaaclab/isaaclab/envs/mdp/init.pyi Stub file updated to export the two new termination functions; consistent with the implementation changes.
source/isaaclab/isaaclab/terrains/terrain_generator.py Replaces box-primitive border with a subdivided flat-grid border matching horizontal_scale; correct geometry but uses a slow Python loop for face construction.
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/velocity_env_cfg.py Startup events (physics_material, add_base_mass, base_com, collider_offsets) moved into the base EventsCfg; collider_offsets is not guarded against Newton backend despite using PhysX-specific root_view methods.
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/anymal_d/rough_env_cfg.py Replaces event-based physics selection with SimulationCfg-based physics preset; Newton solver and collision pipeline correctly configured for Anymal-D rough terrain training.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[AnymalDRoughEnvCfg] -->|sim: SimulationCfg| B[RoughPhysicsCfg preset]
    B -->|default/physx| C[PhysxCfg\ngpu_max_rigid_patch_count]
    B -->|newton| D[NewtonCfg\nMJWarpSolverCfg + CollisionPipeline]

    A -->|inherits| E[LocomotionVelocityRoughEnvCfg]
    E -->|events| F[EventsCfg]
    F --> G[physics_material startup]
    F --> H[add_base_mass startup]
    F --> I[base_com preset\ndefault=EventTerm / newton=None]
    F --> J[collider_offsets startup\n⚠️ no newton guard]

    E -->|terminations| K[TerminationsCfg]
    K --> L[illegal_contact]
    K --> M[body_lin_vel_out_of_limit\nnew - NaN + speed check]

    E -->|scene terrain| N[TerrainGenerator]
    N --> O[_add_terrain_border\nsubdivided flat-grid strips\nhorizontal_scale spacing]
Loading

Reviews (1): Last reviewed commit: "Merge branch 'develop' into newton_rough..." | Re-trigger Greptile

Comment on lines +199 to +206
collider_offsets = EventTerm(
func=mdp.randomize_rigid_body_collider_offsets,
mode="startup",
params={
"asset_cfg": SceneEntityCfg("robot", body_names=".*"),
"contact_offset_distribution_params": (0.01, 0.01),
},
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 collider_offsets not guarded against Newton backend

randomize_rigid_body_collider_offsets internally calls asset.root_view.get_contact_offsets() and asset.root_view.set_contact_offsets(), which are PhysX-specific methods on the PhysX ArticulationView. Newton's ArticulationView does not expose these methods (no matches exist in the Newton module). Unlike base_com, which is correctly disabled for Newton via preset(default=..., newton=None), collider_offsets is added as a plain EventTerm and will be executed for all backends, likely raising an AttributeError during startup in Newton mode.

collider_offsets = preset(
    default=EventTerm(
        func=mdp.randomize_rigid_body_collider_offsets,
        mode="startup",
        params={
            "asset_cfg": SceneEntityCfg("robot", body_names=".*"),
            "contact_offset_distribution_params": (0.01, 0.01),
        },
    ),
    newton=None,
)

Comment on lines +296 to +305
faces = []
for i in range(nx - 1):
for j in range(ny - 1):
v0 = i * ny + j
v1 = v0 + 1
v2 = (i + 1) * ny + j
v3 = v2 + 1
faces.append([v0, v2, v1])
faces.append([v1, v2, v3])
return trimesh.Trimesh(vertices=vertices, faces=np.array(faces, dtype=np.uint32))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Quadratic Python loop for mesh face generation

The nested for i / for j Python loops build faces as a growing list, costing O(nx × ny) loop iterations at the Python level. For a typical border_width=5 m with horizontal_scale=0.1 m, each strip has ~50 × N cells. While this is a one-time startup cost, a vectorised numpy approach avoids it entirely and makes the intent clearer:

i_idx = np.repeat(np.arange(nx - 1), ny - 1)
j_idx = np.tile(np.arange(ny - 1), nx - 1)
v0 = i_idx * ny + j_idx
v1 = v0 + 1
v2 = (i_idx + 1) * ny + j_idx
v3 = v2 + 1
faces = np.column_stack([
    np.stack([v0, v2, v1], axis=1),
    np.stack([v1, v2, v3], axis=1),
]).reshape(-1, 3)
return trimesh.Trimesh(vertices=vertices, faces=faces)

@kellyguo11
Copy link
Copy Markdown
Contributor

@hujc7 could you also take a look at the potential issues the bot review raised? we can incorporate any fixes needed in your PRs as well

@kellyguo11
Copy link
Copy Markdown
Contributor

looks like there are some test failures with this PR as well. @hujc7 I think it may be easier to just cherry-pick the required changes from this PR into your PRs so that we can also address the tests.

_ ERROR collecting source/isaaclab/test/envs/test_manager_based_rl_env_obs_spaces.py _
ImportError while importing test module '/workspace/isaaclab/source/isaaclab/test/envs/test_manager_based_rl_env_obs_spaces.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
_isaac_sim/kit/python/lib/python3.12/importlib/__init__.py:90: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
source/isaaclab/test/envs/test_manager_based_rl_env_obs_spaces.py:25: in <module>
    from isaaclab_tasks.manager_based.locomotion.velocity.config.anymal_c.rough_env_cfg import AnymalCRoughEnvCfg
source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/config/anymal_c/rough_env_cfg.py:8: in <module>
    from isaaclab_tasks.manager_based.locomotion.velocity.velocity_env_cfg import (
E   ImportError: cannot import name 'StartupEventsCfg' from 'isaaclab_tasks.manager_based.locomotion.velocity.velocity_env_cfg' (/workspace/isaaclab/source/isaaclab_tasks/isaaclab_tasks/manager_based/locomotion/velocity/velocity_env_cfg.py)

hujc7 pushed a commit to hujc7/IsaacLab that referenced this pull request Apr 24, 2026
Originally Octi's commit on PR isaac-sim#5225. Cherry-picked here so the rough
terrain stack does not depend on his still-open WIP PR while he is away.

Included from isaac-sim#5225:
- source/isaaclab_tasks/.../config/anymal_d/rough_env_cfg.py:
  Anymal-D SimulationCfg-based RoughPhysicsCfg (MJWarp solver + collision
  pipeline). The shared parent will hoist this in the next commit.
- source/isaaclab_tasks/.../velocity/velocity_env_cfg.py:
  Hoist physics_material, add_base_mass, base_com startup events into
  the shared EventsCfg. base_com guarded with preset(newton=None) per
  ablation A4 (without the gate, 99.99% of episodes terminate from
  body_lin_vel runaway on Newton). Upstream Newton fix
  newton-physics/newton#2332 will let us drop
  the gate once it ships in a release.

Dropped from isaac-sim#5225 (no longer needed):
- collider_offsets startup event in velocity_env_cfg.py: per ablation
  A3 (clobbers shape margin via gap = max(0, contact_offset - margin) =
  0, costing -3.71 reward on Anymal-D) and greptile P1 (PhysX-only
  root_view methods, raises AttributeError on Newton without a guard).
- body_lin_vel_out_of_limit / body_ang_vel_out_of_limit terminations
  and their __init__.pyi exports: were a NaN guard for the Newton
  body_lin_vel runaway when base_com was unguarded. With the
  preset(newton=None) gate on base_com, the runaway no longer occurs
  and the guards are unused.
- terrain_generator.py subdivided flat-grid border: was a workaround
  for Newton triangle-collision failures on the box-primitive border.
  Newton has since improved triangle handling, so the workaround is no
  longer needed.

Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
hujc7 pushed a commit to hujc7/IsaacLab that referenced this pull request Apr 24, 2026
Originally Octi's commit on PR isaac-sim#5225. Cherry-picked here so the rough
terrain stack does not depend on his still-open WIP PR while he is away.

Included from isaac-sim#5225:
- source/isaaclab_tasks/.../config/anymal_d/rough_env_cfg.py:
  Anymal-D SimulationCfg-based RoughPhysicsCfg (MJWarp solver + collision
  pipeline). The shared parent will hoist this in the next commit.
- source/isaaclab_tasks/.../velocity/velocity_env_cfg.py:
  Hoist physics_material, add_base_mass, base_com startup events into
  the shared EventsCfg. base_com guarded with preset(newton=None) per
  ablation A4 (without the gate, 99.99% of episodes terminate from
  body_lin_vel runaway on Newton). Upstream Newton fix
  newton-physics/newton#2332 will let us drop
  the gate once it ships in a release.

Dropped from isaac-sim#5225 (no longer needed):
- collider_offsets startup event in velocity_env_cfg.py: per ablation
  A3 (clobbers shape margin via gap = max(0, contact_offset - margin) =
  0, costing -3.71 reward on Anymal-D) and greptile P1 (PhysX-only
  root_view methods, raises AttributeError on Newton without a guard).
- body_lin_vel_out_of_limit / body_ang_vel_out_of_limit terminations
  and their __init__.pyi exports: were a NaN guard for the Newton
  body_lin_vel runaway when base_com was unguarded. With the
  preset(newton=None) gate on base_com, the runaway no longer occurs
  and the guards are unused.
- terrain_generator.py subdivided flat-grid border: was a workaround
  for Newton triangle-collision failures on the box-primitive border.
  Newton has since improved triangle handling, so the workaround is no
  longer needed.

Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
hujc7 pushed a commit to hujc7/IsaacLab that referenced this pull request Apr 24, 2026
Originally Octi's commit on PR isaac-sim#5225. Cherry-picked here so the rough
terrain stack does not depend on his still-open WIP PR while he is away.

Included from isaac-sim#5225:
- source/isaaclab_tasks/.../config/anymal_d/rough_env_cfg.py:
  Anymal-D SimulationCfg-based RoughPhysicsCfg (MJWarp solver + collision
  pipeline). The shared parent will hoist this in the next commit.
- source/isaaclab_tasks/.../velocity/velocity_env_cfg.py:
  Hoist physics_material, add_base_mass, base_com startup events into
  the shared EventsCfg. base_com guarded with preset(newton=None) per
  ablation A4 (without the gate, 99.99% of episodes terminate from
  body_lin_vel runaway on Newton). Upstream Newton fix
  newton-physics/newton#2332 will let us drop
  the gate once it ships in a release.

Dropped from isaac-sim#5225 (no longer needed):
- collider_offsets startup event in velocity_env_cfg.py: per ablation
  A3 (clobbers shape margin via gap = max(0, contact_offset - margin) =
  0, costing -3.71 reward on Anymal-D) and greptile P1 (PhysX-only
  root_view methods, raises AttributeError on Newton without a guard).
- body_lin_vel_out_of_limit / body_ang_vel_out_of_limit terminations
  and their __init__.pyi exports: were a NaN guard for the Newton
  body_lin_vel runaway when base_com was unguarded. With the
  preset(newton=None) gate on base_com, the runaway no longer occurs
  and the guards are unused.
- terrain_generator.py subdivided flat-grid border: was a workaround
  for Newton triangle-collision failures on the box-primitive border.
  Newton has since improved triangle handling, so the workaround is no
  longer needed.

Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
hujc7 pushed a commit to hujc7/IsaacLab that referenced this pull request Apr 24, 2026
Originally Octi's commit on PR isaac-sim#5225. Cherry-picked here so the rough
terrain stack does not depend on his still-open WIP PR while he is away.

Included from isaac-sim#5225:
- source/isaaclab_tasks/.../config/anymal_d/rough_env_cfg.py:
  Anymal-D SimulationCfg-based RoughPhysicsCfg (MJWarp solver + collision
  pipeline). The shared parent will hoist this in the next commit.
- source/isaaclab_tasks/.../velocity/velocity_env_cfg.py:
  Hoist physics_material, add_base_mass, base_com startup events into
  the shared EventsCfg. base_com guarded with preset(newton=None) per
  ablation A4 (without the gate, 99.99% of episodes terminate from
  body_lin_vel runaway on Newton). Upstream Newton fix
  newton-physics/newton#2332 will let us drop
  the gate once it ships in a release.

Dropped from isaac-sim#5225 (no longer needed):
- collider_offsets startup event in velocity_env_cfg.py: per ablation
  A3 (clobbers shape margin via gap = max(0, contact_offset - margin) =
  0, costing -3.71 reward on Anymal-D) and greptile P1 (PhysX-only
  root_view methods, raises AttributeError on Newton without a guard).
- body_lin_vel_out_of_limit / body_ang_vel_out_of_limit terminations
  and their __init__.pyi exports: were a NaN guard for the Newton
  body_lin_vel runaway when base_com was unguarded. With the
  preset(newton=None) gate on base_com, the runaway no longer occurs
  and the guards are unused.
- terrain_generator.py subdivided flat-grid border: was a workaround
  for Newton triangle-collision failures on the box-primitive border.
  Newton has since improved triangle handling, so the workaround is no
  longer needed.

Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
kellyguo11 added a commit that referenced this pull request Apr 26, 2026
… on Newton (#5248)

# Description

Enables Newton rough-terrain locomotion training on all locomotion
velocity envs (Go1, Go2, A1, Anymal-B/C/D, H1, Cassie, Digit, G1) on top
of [@ooctipus](https://github.com/ooctipus)'s Anymal-D foundation work,
cherry-picked from PR #5225.

## Why this PR exists

PR #5225 (Octi's draft) added Newton support for Anymal-D rough terrain.
The other 9 locomotion envs were left out of scope. Octi is away and his
PR has CI failures, so per maintainer guidance ([Kelly Guo's
comment](#5225 (comment)))
the required changes from #5225 are cherry-picked here so the
rough-terrain stack can move forward without depending on his still-open
WIP PR.

## Dependencies

- PR #5365 — adds `isaaclab.utils.checked_apply`, used by
`NewtonManager.create_builder` to forward `NewtonShapeCfg` onto Newton's
upstream `ShapeConfig`. Required for stable rough-terrain contact.

## 1. Cherry-pick from #5225 (`614ea2dbb74`)

Commit authored by Octi Zhang with `Co-authored-by` trailer. Subset of
#5225 — what's kept and dropped:

| # | Item | Status | Reason |
|---:|---|---|---|
| 1 | `anymal_d/rough_env_cfg.py` Anymal-D Newton config | **KEPT**
(then hoisted into shared parent in commit 2 below) | Defines the Newton
physics shape used for rough terrain |
| 2 | `velocity_env_cfg.py` — hoist `physics_material`, `add_base_mass`,
`base_com` startup events into shared `EventsCfg` | **KEPT** | All envs
need them |
| 3 | `base_com` guard `preset(default=..., newton=None)` | **KEPT** |
Ablation A4 on #5225 (posted 2026-04-17): without the gate, 99.99% of
episodes terminate from `body_lin_vel` runaway on Newton. Upstream
Newton fix newton-physics/newton#2332 will let us drop the guard once it
ships |
| 4 | `velocity_env_cfg.py` — `collider_offsets` startup event |
**DROPPED** | (a) Greptile P1 on #5225: PhysX-only `root_view` methods,
would `AttributeError` on Newton without a guard. (b) Ablation A3:
clobbers the 1cm shape margin set by `RoughPhysicsCfg` (event resets
`gap = max(0, contact_offset − margin) = 0`). Removing it gives
**+3.71** reward on Anymal-D Newton (+16.38 vs A0 baseline +12.47) |
| 5 | `terminations.py` — `body_lin_vel_out_of_limit` /
`body_ang_vel_out_of_limit` + `__init__.pyi` exports | **DROPPED** |
Were a NaN guard for the Newton `body_lin_vel` runaway when `base_com`
was unguarded. With the `preset(newton=None)` gate (item 3), the runaway
no longer occurs and the guards are unused |
| 6 | `terrain_generator.py` subdivided flat-grid border | **DROPPED** |
Was a workaround for Newton triangle-collision failures on the
box-primitive border. Newton has since improved triangle handling, so
the workaround is no longer needed |

## 2. New work — `2a532d1f745`

### 2.1 `NewtonShapeCfg` + `checked_apply` wiring

The single most important Newton setting for rough terrain is **shape
margin**. Without a nonzero margin, non-Anymal-D robots fail to learn
stable contact on triangle-mesh terrain. The previous
`NewtonManager.create_builder` only set `gap = 0.01` and left `margin`
at Newton's upstream default of `0.0`.

This PR adds `NewtonShapeCfg` (the Isaac Lab wrapper) exposing `margin`
and `gap`, and forwards it onto Newton's upstream `ShapeConfig` via
`checked_apply` from PR #5365:

```python
@configclass
class NewtonShapeCfg:
    margin: float = 0.0
    gap: float = 0.01

# in NewtonCfg
default_shape_cfg: NewtonShapeCfg = NewtonShapeCfg()

# in NewtonManager.create_builder
shape_cfg = cfg.default_shape_cfg if isinstance(cfg, NewtonCfg) else NewtonShapeCfg()
checked_apply(shape_cfg, builder.default_shape_cfg)
```

`RoughPhysicsCfg` opts in to
`default_shape_cfg=NewtonShapeCfg(margin=0.01)`.

### 2.2 Hoist `RoughPhysicsCfg` into shared base

Octi's per-env Anymal-D `RoughPhysicsCfg` (MJWarp solver + collision
pipeline) is hoisted into `LocomotionVelocityRoughEnvCfg.sim` so every
rough-terrain env inherits identical Newton physics. Per-env files
become minimal robot-only deltas.

### 2.3 Per-env Newton-only tweak

- **Go1**: leg armature preset for joint stability on lightweight
quadruped.

### 2.4 Mass randomization rewrite

Replace `EventsCfg.add_base_mass`'s additive `(-5, 5)` kg default with
multiplicative `(1/1.25, 1.25)` log-uniform scale (`operation="scale"`,
`distribution="log_uniform"`). Scale-invariant across robot sizes,
geometric mean 1.0, no per-robot kg overrides needed.

Per-robot ablation, Newton, 1500-iter (small quadrupeds early-aborted at
iter 300):

| # | Robot | new log-uniform | old additive baseline | ratio | verdict
|
|---:|---|---:|---:|---|---|
| 1 | A1 (iter 300) | 10.00 | 3.25 | **3.08×** | pass — driven by bias
removal (old `(-1, 3)` had +10% mean bias on A1's 10 kg base) |
| 2 | Go1 (iter 300) | 22.29 | 16.30 | 1.37× | pass |
| 3 | Anymal-B (iter 300) | 12.47 | 10.92 | 1.14× | pass |
| 4 | Anymal-C (iter 300) | 14.64 | 12.31 | 1.19× | pass |
| 5 | Go2 @ 1499 | 24.71 | 18.58 | 1.33× | pass |
| 6 | Anymal-D @ 1499 | 16.09 | 15.62 | 1.03× | pass |
| 7 | H1 @ 1499 (biped) | 24.02 | 23.58 | 1.02× | pass |
| 8 | Cassie @ 1499 sym25 | 14.15 | 23.93 (mass rand off) | 0.59× |
**regression — fixed in dependent PR #5298 with `(1.0, 1.25)`
heavier-only override** |

Cassie sensitivity is closed-loop Achilles + hip PD response:
lighter-than-nominal pelvis destabilizes; heavier-only `(1.0, 1.25)`
recovers 90% of the reward and pushes ep_len higher (932 vs 910
baseline). Sub-ablation table is in #5298.

Raw logs, checkpoints, and config snapshots preserved at
`~/workspaces/data/2026-04-21_mass-rand-scale/` (per-robot
`<robot>_newton_1500.log`, `key.md`, `status.md`, `run.sh` reproducer).

## Versions

- `isaaclab_newton` 0.5.21 → 0.5.22
- `isaaclab_tasks` 1.5.24 → 1.5.25

## Type of change

- New feature (non-breaking).

## Checklist

- [x] Pre-commit checks pass
- [x] CHANGELOG + extension.toml bumped on both `isaaclab_newton` and
`isaaclab_tasks`
- [x] Co-author credit for [@ooctipus](https://github.com/ooctipus) on
the cherry-pick commit
- [x] Ablation evidence cited in commit messages

---------

Signed-off-by: Kelly Guo <kellyg@nvidia.com>
Co-authored-by: Octi Zhang <zhengyuz@nvidia.com>
Co-authored-by: Kelly Guo <kellyg@nvidia.com>
@ooctipus ooctipus closed this Apr 27, 2026
@github-project-automation github-project-automation Bot moved this from In review to Done in Isaac Lab Apr 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation isaac-lab Related to Isaac Lab team isaac-mimic Related to Isaac Mimic team

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants